unit opengls;

{*
OpenGl related stuff, especially library constant names which is platform dependend (different in windows and linux)
}

interface

uses SysUtils, Classes, gl, glu, glut, keyboards;

const
  {$IFDEF WINDOWS}
  LibraryGl = 'opengl32.dll';
  LibraryGlu = 'glu32.dll';
  {$ENDIF}
  {$IFDEF LINUX}
  LibraryGl = 'GL';
  LibraryGlu = 'GLU';
  {$ENDIF}

type
  ExGlutTerminate = class(Exception);

var
  ScreenWidth: integer;
  ScreenHeight: integer;
  FrontSpecular: array[0..3] of glfloat;
  FrontShininess: array[0..0] of glfloat;
  Ambient: array[0..3] of glfloat;
  Diffuse: array[0..3] of glfloat;
  PositionLight: array[0..3] of glfloat;
  ModelAmbient: array[0..3] of glfloat;
  FogColor: array[0..3] of glfloat;
  Terminating: boolean;
  SplashProgress : integer = 0;
  SplashMax : integer = 100;

procedure ResizeWindow(NewWidth, NewHeight: LongInt); cdecl;
procedure InitOpenGl;
procedure InitLights;
procedure UseLights;
function WorldToScreen(x, y, z: real; var sx: integer; var sy: integer): boolean;
function Splash : boolean;

implementation

function WorldToScreen(x, y, z: real; var sx: integer; var sy: integer): boolean;
var
  mx, my, mz: GLDouble;
  Projection, ModelView: T16dArray;
  Viewport: TViewPortArray;
begin
  // convert 3D to screen coordinates, result is in sx,sy, [0,0] is in upper left corner, return true if point is visible
  glGetDoublev(GL_PROJECTION_MATRIX, @Projection);
  glGetDoublev(GL_MODELVIEW_MATRIX, @ModelView);
  glGetIntegerv(GL_VIEWPORT, @Viewport);
  gluProject(x, y, z, ModelView, Projection, Viewport, @mx, @my, @mz);
  sx := trunc(mx);
  sy := ScreenHeight - trunc(my);
  result := (mz < 1) and (sx >= 0) and (sy >= 0) and (sx <= ScreenWidth) and (sy <= ScreenHeight);
end;

procedure ResizeWindow(NewWidth, NewHeight: LongInt); cdecl;
begin
  glViewport(0, 0, NewWidth, NewHeight);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45, NewWidth / NewHeight, 1.0, 5000);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  ScreenWidth := NewWidth;
  ScreenHeight := NewHeight;
  glutShowWindow;
end;

procedure GlutInit2;
var
  argc: integer;
  argvTemp,
  argv: PPChar;
  i: integer;
begin
  argc:= ParamCount + 1;
  GetMem(argv, SizeOf(PChar)*(argc+1));
  argvTemp:= argv;
  try
    for i := 0 to ParamCount do begin
      argvTemp^ := PChar(ParamStr(i));
      inc(argvTemp);
    end;
    argvTemp^ := nil;
    glutInit(@argc, argv);
  finally
    FreeMem(argv);
  end;
end;

procedure InitOpenGl;
begin
  // OpenGL stuff
  Terminating := false;
  {$IFDEF FPC}
  glutInit(@argc,argv);
  {$ELSE}
  glutInit2;
  {$ENDIF}
  glutInitDisplayMode(GLUT_RGBA or GLUT_DOUBLE or GLUT_DEPTH);
  glutInitWindowSize(ScreenWidth, ScreenHeight);
  glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - ScreenWidth) div 2,
    (glutGet(GLUT_SCREEN_HEIGHT) - ScreenHeight) div 2);
  glutCreateWindow('Client');
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glShadeModel(GL_SMOOTH);
  glClearDepth(1);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  glDepthFunc(GL_LESS);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45, 1.3, 1, 5000);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  InitLights;
  UseLights;
  RegisterKeyboard;
end;

procedure InitLights;
begin
  FrontShininess[0] := 100.0;
  FrontSpecular[0] := 1.0;
  FrontSpecular[1] := 21.0;
  FrontSpecular[2] := 1.0;
  FrontSpecular[3] := 1.0;
  Ambient[0] := 2.0;
  Ambient[1] := 2.0;
  Ambient[2] := 2.0;
  Ambient[3] := 1.0;
  Diffuse[0] := 2.0;
  Diffuse[1] := 2.0;
  Diffuse[2] := 2.0;
  Diffuse[3] := 1.0;
  PositionLight[0] := 140;
  PositionLight[1] := 840;
  PositionLight[2] := 690;
  PositionLight[3] := 1;
  ModelAmbient[0] := 0.2;
  ModelAmbient[1] := 0.2;
  ModelAmbient[2] := 0.2;
  ModelAmbient[3] := 0.5;
  FogColor[0] := 1.0;
  FogColor[1] := 1.0;
  FogColor[2] := 1.0;
  FogColor[3] := 0;
end;

procedure UseLights;
begin
  glLightfv(GL_LIGHT0, GL_AMBIENT, @Ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, @Diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, @PositionLight);
  glLightfv(GL_LIGHT0, GL_SPECULAR, @FrontSpecular);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ModelAmbient);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, @FrontShininess);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, @FrontSpecular);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);
  glEnable(GL_CULL_FACE); 
end;

function Splash : boolean;
var k : real;
begin
  result := SplashProgress <= SplashMax;
  if not result then
    exit; 
  inc(SplashProgress);
  k := 2*4.7*SplashProgress/SplashMax;
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glLoadIdentity;
  glTranslatef(0, 0, -2);
  glDisable(GL_LIGHTING);
  glBegin(GL_QUADS);
  // white background
  glColor3f(1,1,1);
  glVertex3f(+5, -0.5, -20);
  glVertex3f(+5, +0.5, -20);
  glVertex3f(-5, +0.5, -20);
  glVertex3f(-5, -0.5, -20);
  // black inside
  glColor3f(0.2,0.2,0.2);
  glVertex3f(+4.7, -0.4, -19);
  glVertex3f(+4.7, +0.4, -19);
  glVertex3f(-4.7+k, +0.4, -19);
  glVertex3f(-4.7+k, -0.4, -19);
  // blue progress
  glColor3f(0.2,0.4,0.8);
  glVertex3f(-4.7+k, -0.4, -19);
  glVertex3f(-4.7+k, +0.4, -19);
  glVertex3f(-4.7, +0.4, -19);
  glVertex3f(-4.7, -0.4, -19);
  // done
  glEnd;
  glutSwapBuffers;
end;

initialization

  ScreenWidth := 640;
  ScreenHeight := 480;

finalization

end.